﻿using Polly;
using System;
using System.Threading.Tasks;

namespace Kz.Telegram.Bot.Infrastructure.Helpers
{
    /// <summary>
    /// Polly帮助类
    /// </summary>
    public class PollyHelper
    {
        private static readonly string logFolder = "Polly";

        /// <summary>
        /// 故障重试
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <typeparam name="TException"></typeparam>
        /// <param name="retryCount"></param>
        /// <param name="execute"></param>
        /// <param name="errBack"></param>
        /// <returns></returns>
        public static async Task<TResult> Retry<TResult, TException>(int retryCount, Func<Task<TResult>> execute, Action<int, Exception, TimeSpan> errBack = null) where TException : Exception
        {
            int total = 0;
            var retryPolicy = Policy<TResult>.Handle<TException>().WaitAndRetryAsync(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, ts, context) =>
            {
                total++;
                var log = $"执行失败: 重试次数: {total} 时间:{ts}   异常来自: {ex.GetType().Name}";
                errBack?.Invoke(total, ex.Exception, ts);
            });
            return await retryPolicy.ExecuteAsync(execute);
        }

        /// <summary>
        /// 故障重试 自定义默认返回
        /// </summary>
        /// <typeparam name="TResult">返回类型</typeparam>
        /// <typeparam name="TResult">返回类型</typeparam>
        /// <typeparam name="TException">捕获异常类型</typeparam>
        /// <param name="logName">日志文件名</param>
        /// <param name="defaultVal">自定义默认返回值</param>
        /// <param name="retryCount">等待秒,乘积等待</param>
        /// <param name="execute">执行委托</param>
        /// <returns></returns>
        public static async Task<TResult> RetryAndFallback<TResult, TException>(string logName, TResult defaultVal, int retryCount, Func<Task<TResult>> execute, Action<string> errBack = null) where TException : Exception
        {
            int total = 0;
            var fallBackPolicy = Policy<TResult>.Handle<TException>().FallbackAsync(defaultVal);
            var politicaWaitAndRetry = Policy<TResult>.Handle<TException>().WaitAndRetryAsync(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, ts) =>
            {
                total++;
                var log = $"执行失败: 重试次数: {total} 时间:{ts}   异常来自: {ex.GetType().Name}";
                //LogHelper.INF(logFolder, logName, log);
                errBack?.Invoke(log);
            });
            var mixedPolicy = Policy.WrapAsync(fallBackPolicy, politicaWaitAndRetry);
            var mixedResult = await mixedPolicy.ExecuteAsync(execute);

            //如果与自定义默认返回值不一致时，执行成功 ,不太严谨 ??
            if (mixedResult.Equals(defaultVal))
            {
                //LogHelper.INF(logFolder, logName, $"执行成功");
            }
            return mixedResult;
        }
    }
}